// Clipboard.h
// 2008/07/09

#pragma once
#include "include/LuoSnapShot.h"

// Clipboard
class Clipboard {

	bool m_bOpened;

	CStringW m_BaseName;
	CStringW m_BasePath;

	CStringW m_MimeType;
	ULONG_PTR token_;
public:

	explicit Clipboard(const LuoSnapShot::ClipDump* item) :
		m_bOpened(false),
		token_(0)
	{
		m_BaseName = item->BaseName;
		m_BasePath = item->BasePath;

		m_MimeType = item->MimeType;

		// Init GDI++
		Gdiplus::GdiplusStartupInput input;
		Gdiplus::GdiplusStartupOutput output = { 0, 0};
		Gdiplus::Status ret = Gdiplus::GdiplusStartup(&token_, &input, &output);
		if (ret != Gdiplus::Ok) {
			::MessageBox(NULL, _T("Fail to init GDI++!"), _T("Error"), MB_OK);
		}
	}

	~Clipboard()
	{
		if (m_bOpened) {
			CloseClipboard();
		}

		// Shut down GDI++
		Gdiplus::GdiplusShutdown(token_);
	}

	HRESULT Dump()
	{
		if (!OpenClipboard(0)) {
			DWORD error = GetLastError();
			return HRESULT_FROM_WIN32(error);
		}

		m_bOpened = true;

#ifdef _DEBUG
		{
			ATLTRACE("CF_DUMP:\n");

			UINT cFormat = 0;
			for (; ; ) {
				cFormat = EnumClipboardFormats(cFormat);
				if (cFormat == 0) {
					break;
				}

				WCHAR name[0x100];
				name[0] = L'\0';
				GetClipboardFormatNameW(cFormat, name, 0x100);

				CStringA an(name);
				if (an.IsEmpty()) {
					if (cFormat == CF_BITMAP) an = "CF_BITMAP";
				}

				ATLTRACE("CF : %04X - %s\n", cFormat, (const char*)an);
			}
		}
#endif

		CLSID clsid = { 0 };
		GetEncoderClsid(m_MimeType, &clsid);

		if (!IsClipboardFormatAvailable(CF_BITMAP)) {
			ATLTRACE("CD: NO_BITMAP\n");
			return S_FALSE;
		}

		HBITMAP hBitmap = static_cast<HBITMAP>(GetClipboardData(CF_BITMAP));
		if (hBitmap == 0) {
			DWORD error = GetLastError();
			return HRESULT_FROM_WIN32(error);
		}

		{
			Gdiplus::Bitmap srcBitmap(hBitmap, 0);

			INT cx = srcBitmap.GetWidth();
			INT cy = srcBitmap.GetHeight();

			ATLTRACE("CD: Size (%d, %d)\n", cx, cy);

			{
				CStringW path = GetFilePath();

				Gdiplus::Status st = srcBitmap.Save(path, &clsid);
				if (st != Gdiplus::Ok) {
					return E_FAIL;
				}
			}
		}

		ATLTRACE("CD: DONE\n");

		return S_OK;
	}

private:

	static int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
	{
		UINT num  = 0;
		UINT size = 0;
		Gdiplus::GetImageEncodersSize(&num, &size);
		if (size == 0) return -1;

		Gdiplus::ImageCodecInfo* pImageCodecInfo = (Gdiplus::ImageCodecInfo*)malloc(size);
		Gdiplus::GetImageEncoders(num, size, pImageCodecInfo);

		for (UINT i = 0; i < num; i++) {
			if (wcscmp(pImageCodecInfo[i].MimeType, format) == 0) {
				*pClsid = pImageCodecInfo[i].Clsid;
				free(pImageCodecInfo);
				return i;
			}
		}

		free(pImageCodecInfo);
		return -1;
	}

	CStringW GetFilePath()
	{
		SYSTEMTIME st = { 0 };
		GetLocalTime(&st);

		CStringW dt;
		dt.Format(
			L"%04d%02d%02d-%02d%02d%02d",
			st.wYear, st.wMonth, st.wDay,
			st.wHour, st.wMinute, st.wSecond);

		CStringW pre;
		CStringW ext;

		int idx = m_BaseName.Find(L".");
		if (idx >= 0) {
			pre = m_BaseName.Left(idx);
			ext = m_BaseName.Mid(idx);
		} else {
			pre = m_BaseName;
		}
		
		// create save path.
		if(!pre.IsEmpty() && !ext.IsEmpty()) {
			WCHAR path[MAX_PATH];
			PathCombineW(path, m_BasePath, m_BaseName);
			return CStringW(path);
		}

		for (int i = 1; i <= 99; i++) {
			CStringW name;

			if (i == 1) {
				name.Format(L"%s%s%s", (LPCWSTR)pre, dt, (LPCWSTR)ext);
			} else {
				name.Format(L"%s%s(%d)%s", (LPCWSTR)pre, dt, i, (LPCWSTR)ext);
			}

			WCHAR path[MAX_PATH];
			PathCombineW(path, m_BasePath, name);

			WIN32_FIND_DATA fd = { 0 };
			HANDLE hFind = FindFirstFileW(
				path,
				&fd);
			if (hFind == INVALID_HANDLE_VALUE) {
				return CStringW(path);
			}

			FindClose(hFind);
		}

		return CStringW();
	}

}; // Clipboard

